/*  Controller.m --- Main controller object for the TextLab program.
**  Authors: Bruce Blumberg and Ali Ozer, NeXT Developer Support Group
*/

// Controller object is the central object in TextLab.  It manages the
// windows, open/save panels, and menu commands. 

#import "Controller.h"
#import "TextView.h"
#import <appkit/Application.h>
#import <objc/typedstream.h>

@implementation Controller

// We subclass the Controller "new" so that we can set the application's
// delegate to point to the Controller object. This way open file
// requests from the Workspace get routed to the Controller's 
// appOpenFile:type: and appAcceptsAnotherFile methods.

+ new
{
  self = [super new];
  [NXApp setDelegate:self];
  
  // create instances of support objects
  openReq = [OpenPanel new];
  saveReq = [SavePanel new];
  
  return self;
}

- showError: (char *)errorMessage
{
  NXRunAlertPanel (NULL, errorMessage,"OK",NULL,NULL);
}

// newTextView: is invoked in response to a new empty window request. It
// creates a new window containing a TextView. Note that we want new windows 
// to be offset from each other by some amount; hence the use of wRect.

#define ORIGX 100.0
#define ORIGY 100.0
static NXRect wRect = {{ORIGX, ORIGY},{500.0,400.0}};

- newTextView:sender
{   
    id newTextView;
    NXOffsetRect(&wRect, 20.0, -20.0);
    if (wRect.origin.y < 0) {wRect.origin.y = ORIGY; wRect.origin.x = ORIGX;}
    newTextView = [TextView newFrame:&wRect];
    [[newTextView window] setDelegate:self];     
    return self;
}

// appAcceptsAnotherFile is an application delegate method which 
// returns whether it is OK for the application to try to open more files
// with the appOpenFile:type: method. TextLab can indeed open multiple
// windows, so we return YES.

-(BOOL) appAcceptsAnotherFile:sender
{
  return (YES);
}


// appOpenFile:type: is called to open the specified file. It is normally
// called by the Application object in response to open requests from the
// Workspace. Here we also route the open requests from the OpenPanel
// to this method (see openRequest:).

-(int) appOpenFile:(char *)fileName type:(char *)fileType
{
   return [self openFile:fileName];  
}

// openRequest: opens a new file. It puts up a open panel, and, if the user
// doesn't cancel, it reads the specified archive file. If the selected file
// is not a proper archive file, then openRequest: will complain.

- openRequest:sender
{
    const char *fileName;
    const char *const types[2] = {"tl", NULL};
    int ok;

    if ([openReq runModalForTypes:types] && (fileName =[openReq filename])) {
	[self openFile:fileName];
    }
    else
	[self showError:"Not a valid textlab file."];
    return self;
}

-(int) openFile:(const char *)fileName
{
   id win;
   NXTypedStream *typedStream;
   
    if(!(typedStream = NXOpenTypedStreamForFile(fileName,NX_READONLY))){
	[self showError:"error on opening file"];
	return NO;
    }
    else {
	win = NXReadObject(typedStream);
	NXCloseTypedStream(typedStream);
	[win setTitle:fileName];
        [[win display] makeKeyAndOrderFront:self];
	return YES;
    } 
}


// saveRequest: saves the current window under its default name (found in
// the title bar). Note that if the title bar is empty or the default title
// is "Untitled" then saveRequest: will put up a save panel, giving the user
// a chance to specify a real title.

- saveRequest:sender
{
    const char *fileName;
    id curWin = [NXApp mainWindow];

    if (curWin == nil) 
	[self showError:"No active window to save."];
    else {
	// Check to see if the current window is titled and the title is not
	// "Untitled". If so, save the file, else put up a save panel...
	fileName = [curWin title];
	if (strcmp (fileName, "Untitled"))
	    [self saveWindow:curWin inPath:fileName];
	else [self saveInRequest:sender];
    }
    return self;
}

// saveInRequest: gives the user a chance to save the current window
// under a new name. 

- saveInRequest:sender
{
    const char *fileName;
    id curWin;

    /*			EXERCISE #4a 				*
    *	Add code to respond to an saveInRequest			*
    *	Hint:  Use a SavePanel to get the file name and 	*
    *	directory where the document should be saved.		*
    *	You will note that the saveReq instance variable	*
    *	was initialized in the +new method of Controller	*
    *	to point to a SavePanel. SavePanels will automatically 	*
    *	append an extension on the file name specified by the	*
    *	the user if you have previously sent it the 		*
    *	setRequiredFileType: message passing it the appropriate *
    *	extension. Once you have done this, you should send it a*
    *	a method called runModalForDirectory:file: which	*
    *	makes theSavePanel visible and which make it    	*					
    *	a modal window. Once that method returns you can	*
    *	query the SavePanel for the file name  			*
    *	selected (look in the spec sheets for SavePanel).	*
    *	Assuming you used setRequiredFileType: earlier the file *
    *	name returned will have the appropriate extension	*
    *	Finally you should call the saveWindow:inPath: 		*
    *	method of Controller which will actually save the 	*
    *   document. Note the first argument to saveWindow is	* 				    
    *	the id of the window containing the document to be 	*
    *	saved. Look at the specSheets for the Application 	*
    *	object to see what method to use to get the id of the 	*
    *	current main window. In fact you will probably want to	*
    *	get the id of the main window first thing in here so 	*
    *	you can use its title as the default file name for	*
    *	the file in which the window will be stored		*/
    
    return self;
}


// saveWindow writes a window out the archive file whose name is specified
// by the second argument. The title of the current window is also set 
// accordingly.

- saveWindow:(id)win inPath:(const char *)name
{	
   NXTypedStream *typedStream;
    
   [win setTitle:name];
    
    /*			EXERCISE #4b 				*
    *	Add code to store win and its contents			*
    *	in an file called name.Hint: you will want to 		*
    *   use typedStreams to write out the window and its	*
    *	contents. The code to do this will look similar to the 	*
    *	code in openFile: so be sure to look at that code. You	*
    *	will need to do 3 things. 1) Open a typedStream on a 	*
    *	file named name using NXOpenTypedStreamForFile()	*
    *	2) Write out the window to the typedStream using	*
    *	NXWriteRootObject(). 3) Close the typedStream.		*/
    
    return self;
}

// Printing is rather simple; just send printPSCode: to the text view
// you wish to print. The print panel will automatically pop up and unless
// the user cancels the printout the text view will be printed.

- printRequest:sender
{
    /*			EXERCISE 5 				*
    *	Add code to assign curText to point to			*
    *	view which should be printed. Hint: it is the		*
    *	docView of the TextView in the application's		*
    *	mainWindow. Look at the spec sheets for the		*
    *	Application class to see how to get the id of		*
    *	the current main window					*/

    id curText = nil;

    if (curText == nil) [self showError:"No active window to print."];
    else {
	/*		EXERCISE 5b				*
	* 	Add code to print view				*
	*							*
	*							*/ 
    }
    return self;
}

// closeRequest closes the current window by simulating a click on the
// closebutton. A check should probably be added to give the user the 
// option of saving the window before closing

- closeRequest:sender
{
   [[NXApp mainWindow] performClose:sender];
   return self;
}

// This method will get called before a window is closed and
// will give the user an opportunity to save their file. It then returns
// self indicating that the window may be closed.

- windowWillClose:(id)whichWin
{
    return self;
}
    
@end
